/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.dx.ssa;

import com.android.dx.rop.code.RegisterSpec;
import com.android.dx.util.IntList;

/**
 * This class maps one register space into another, with each mapping built up
 * individually and added via addMapping()
 */
public class BasicRegisterMapper extends RegisterMapper {

	/** indexed by old register, containing new name */
	private IntList oldToNew;

	/** running count of used registers in new namespace */
	private int runningCountNewRegisters;

	/**
	 * Creates a new OneToOneRegisterMapper.
	 * 
	 * @param countOldRegisters
	 *            the number of registers in the old name space
	 */
	public BasicRegisterMapper(int countOldRegisters) {
		oldToNew = new IntList(countOldRegisters);
	}

	/** {@inheritDoc} */
	@Override
	public int getNewRegisterCount() {
		return runningCountNewRegisters;
	}

	/** {@inheritDoc} */
	@Override
	public RegisterSpec map(RegisterSpec registerSpec) {
		if (registerSpec == null) {
			return null;
		}

		int newReg;
		try {
			newReg = oldToNew.get(registerSpec.getReg());
		} catch (IndexOutOfBoundsException ex) {
			newReg = -1;
		}

		if (newReg < 0) {
			throw new RuntimeException("no mapping specified for register");
		}

		return registerSpec.withReg(newReg);
	}

	/**
	 * Returns the new-namespace mapping for the specified old-namespace
	 * register, or -1 if one exists.
	 * 
	 * @param oldReg
	 *            {@code >= 0;} old-namespace register
	 * @return new-namespace register or -1 if none
	 */
	public int oldToNew(int oldReg) {
		if (oldReg >= oldToNew.size()) {
			return -1;
		}

		return oldToNew.get(oldReg);
	}

	/** {@inheritDoc} */
	public String toHuman() {
		StringBuilder sb = new StringBuilder();

		sb.append("Old\tNew\n");
		int sz = oldToNew.size();

		for (int i = 0; i < sz; i++) {
			sb.append(i);
			sb.append('\t');
			sb.append(oldToNew.get(i));
			sb.append('\n');
		}

		sb.append("new reg count:");

		sb.append(runningCountNewRegisters);
		sb.append('\n');

		return sb.toString();
	}

	/**
	 * Adds a mapping to the mapper. If oldReg has already been mapped,
	 * overwrites previous mapping with new mapping.
	 * 
	 * @param oldReg
	 *            {@code >= 0;} old register
	 * @param newReg
	 *            {@code >= 0;} new register
	 * @param category
	 *            {@code 1..2;} width of reg
	 */
	public void addMapping(int oldReg, int newReg, int category) {
		if (oldReg >= oldToNew.size()) {
			// expand the array as necessary
			for (int i = oldReg - oldToNew.size(); i >= 0; i--) {
				oldToNew.add(-1);
			}
		}

		oldToNew.set(oldReg, newReg);

		if (runningCountNewRegisters < (newReg + category)) {
			runningCountNewRegisters = newReg + category;
		}
	}
}
